package com.ying.user.service.impl;

import cn.hutool.core.util.RandomUtil;
import com.ying.common.entity.Response;
import com.ying.user.service.MmVerifyCodeService;
import com.ying.user.utils.VerifyCodeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.InputStreamResource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.*;
import org.springframework.stereotype.Service;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Objects;

@Service
public class MmVerifyCodeServiceImpl implements MmVerifyCodeService {

    private static final Logger logger = LoggerFactory.getLogger(MmVerifyCodeServiceImpl.class);

    private static final String KEY = "verifyCode:";

    private static final String IMAGE_FORMAT = "png";

    private static final long VERIFY_CODE_EXPIRE_TIMEOUT = 60000L;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public Response verify(String code) {
        String lastVerifyCodeWithTimestamp = find(KEY + code);
        // 如果没有验证码，则随机生成一个
        if (lastVerifyCodeWithTimestamp == null) {
            lastVerifyCodeWithTimestamp = appendTimestamp(RandomUtil.randomNumbers(VerifyCodeUtils.getLen()));
        }
        String[] lastVerifyCodeAndTimestamp = lastVerifyCodeWithTimestamp.split("#");
        String lastVerifyCode = lastVerifyCodeAndTimestamp[0];
        long timestamp = Long.parseLong(lastVerifyCodeAndTimestamp[1]);
        if (timestamp + VERIFY_CODE_EXPIRE_TIMEOUT < System.currentTimeMillis()) {
            remove(KEY + code);
            return new Response().failure("验证码已过期");
        } else if (!Objects.equals(code, lastVerifyCode)) {
            return new Response().failure("验证码输入有误");
        }
        return new Response().success("验证码输入正确");
    }

    @Override
    public void save(String key, String code) {
        stringRedisTemplate.opsForValue().set(key, code);
    }

    @Override
    public String find(String key) {
        return stringRedisTemplate.opsForValue().get(key);
    }

    @Override
    public void remove(String key) {
        stringRedisTemplate.delete(key);
    }

    @Override
    public HttpEntity image() {
        String verifyCode = RandomUtil.randomNumbers(VerifyCodeUtils.getLen());
        String verifyCodeWithTimestamp = appendTimestamp(verifyCode);
        Image image = imageWithDisturb(verifyCode);
        save(KEY + verifyCode, verifyCodeWithTimestamp);

        InputStreamResource inputStreamResource = imageToInputStreamResource(image, IMAGE_FORMAT);
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.set("Pragma", "No-cache");
        httpHeaders.set("Cache-Control", "no-cache");
        return ResponseEntity.status(HttpStatus.OK)
                .headers(httpHeaders)
                .contentType(MediaType.IMAGE_PNG)
                .body(inputStreamResource);
    }

    @Override
    public Image imageWithDisturb(String string) {
        return imageWithDisturb(string, VerifyCodeUtils.getWidth(), VerifyCodeUtils.getHeight());
    }

    /**
     * 添加时间戳
     */
    private static String appendTimestamp(String string) {
        return string + "#" + System.currentTimeMillis();
    }

    /**
     * 图像转流
     */
    private static InputStreamResource imageToInputStreamResource(Image image, String format) {
        try {
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            ImageIO.write((RenderedImage) image, format, byteArrayOutputStream);
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
            return new InputStreamResource(byteArrayInputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 图像与干扰
     */
    private Image imageWithDisturb(String string, int width, int height) {
        BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics2D g2d = (Graphics2D) bi.getGraphics();
        // 填充背景
        g2d.setColor(Color.WHITE);
        g2d.fillRect(0, 0, width, height);
        // 抗锯齿
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        // 画干扰圆
        VerifyCodeUtils.drawOval(2, g2d);
        // 画干扰线
        g2d.setStroke(new BasicStroke(2f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
        VerifyCodeUtils.drawBesselLine(1, g2d);

        FontMetrics fontMetrics = g2d.getFontMetrics();
        // 每一个字符所占的宽度
        int fW = width / string.length();
        // 字符的左右边距
        int fSp = (fW - (int) fontMetrics.getStringBounds("W", g2d).getWidth()) / 2;
        for (int i = 0; i < string.length(); i++) {
            g2d.setColor(VerifyCodeUtils.color());
            // 文字的纵坐标
            int fY = height - ((height - (int) fontMetrics.getStringBounds(String.valueOf(string.charAt(i)), g2d).getHeight()) >> 1);
            g2d.drawString(String.valueOf(string.charAt(i)), i * fW + fSp + 3, fY - 3);
        }
        g2d.dispose();
        return bi;
    }

}
